#include "scheduler.hpp"
#include "logger.hpp"
#include "f_engine.hpp"

#include <boost/bind.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/foreach.hpp>
#include <string>

boost::shared_ptr<if_system_t> scheduler_t::create(f_engine_t *engine)
{
    boost::shared_ptr<scheduler_t> ret(new scheduler_t(engine));
    logger::log(DEBUG, "[Scheduler] Scheduler system created.");
    return ret;
}

boost::asio::io_service &scheduler_t::workers_svc()
{
    return worker_svc_;
}

scheduler_t::scheduler_t(f_engine_t *engine) :
    engine_(engine)
{
}

scheduler_t::~scheduler_t()
{
    logger::log(DEBUG, "[Scheduler] Scheduler system destruction...");
    stop();
}

void scheduler_t::set_timer(handler_t handler, boost::posix_time::time_duration &time)
{
    boost::shared_ptr<boost::asio::deadline_timer> timer_ptr(new boost::asio::deadline_timer(timer_svc_));
    timer_ptr->expires_from_now(time);
    timer_ptr->async_wait(boost::bind(&scheduler_t::timer_handler, this, timer_ptr, handler, _1));
}

void scheduler_t::exec_by_worker(handler_t handler)
{
    worker_svc_.post(handler);
}

void scheduler_t::timer_handler(boost::shared_ptr<boost::asio::deadline_timer> timer_holder, handler_t &handler, const boost::system::error_code& error)
{
    if (!error)
    {
        worker_svc_.post(handler);
    }
    else
    {
        logger::log(CRITICAL) << "[Scheduler] Error handling timer.";
    }
}

void scheduler_t::worker_thr_func(size_t num)
{
    std::string name("sch_work:");
    name += boost::lexical_cast<std::string>(num);
    logger::thr_desc_t name_thr(name);

    logger::log(DEBUG, "[Scheduler] Starting worker thread.");

    bool restart_on_error = true;

    while (restart_on_error)
    {
        try
        {
            boost::asio::io_service::work work(worker_svc_);
            worker_svc_.run();
            //correct shutdown
            restart_on_error = false;
        }
        catch (std::exception &e)
        {
            logger::log(CRITICAL) << "[Scheduler] Error in scheduler worker: " << e.what();
        }
        catch (...)
        {
            logger::log(CRITICAL) << "[Scheduler] Unknown error in scheduler worker";
        }
    }

    logger::log(DEBUG, "[Scheduler] Worker thread interrupted normally.");
}

void scheduler_t::timer_thr_func()
{
    logger::thr_desc_t name_thr("sch_timer");

    logger::log(DEBUG, "[Scheduler] Starting timer thread.");

    engine_->report_started();

    engine_->wait_all_systems();

    bool restart_on_error = true;

    while (restart_on_error)
    {
        try
        {
            boost::asio::io_service::work work(timer_svc_);
            timer_svc_.run();
            //correct shutdown
            restart_on_error = false;
        }
        catch (std::exception &e)
        {
            logger::log(CRITICAL) << "[Scheduler] Error in scheduler timer thread: " << e.what();
        }
        catch (...)
        {
            logger::log(CRITICAL) << "[Scheduler] Unknown error in scheduler timer thread";
        }
    }

    logger::log(DEBUG, "[Scheduler] Timer thread interrupted normally.");
}

void scheduler_t::start()
{
    logger::log(DEBUG, "[Scheduler] Starting system.");

    size_t workers_count = engine_->system<config_t> ().tree.get<size_t> ("Engine.WorkThreads");

    for (size_t i = 0; i < workers_count; ++i)
    {
        workers_.push_back(boost::shared_ptr<boost::thread>(new boost::thread(&scheduler_t::worker_thr_func, this, i)));
    }
    timer_thr_ = boost::thread(&scheduler_t::timer_thr_func, this);
}

void scheduler_t::stop()
{

    worker_svc_.stop();
    timer_svc_.stop();

    if (timer_thr_.joinable())
    {
        timer_thr_.join();
    }

    BOOST_FOREACH( boost::shared_ptr<boost::thread> &thr, workers_)
                {
                    if (thr->joinable())
                    {
                        thr->join();
                    }
                }

}

